/* Copyright 2008, Arun Zaheeruddin (a.reginald@gmail.com)
 * 
 * Licensed under the GNU Lesser General Public License, Version 3.0 (the "License").
 * A copy of the License may be obtained from http://www.gnu.org/licenses/lgpl-3.0.txt
 * unless it is otherwise distributed with the source-code (the "Source").
 * Consider reviewing the License for further details about using this Source in your
 * applications and projects.
 *
 * @author Arun Zaheeruddin
 * @version 0.1.0.0
 */

package com.db2object.data.odbc;
import com.db2object.data.DataTable;
import com.db2object.data.DataColumn;
import java.sql.DriverManager;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.Statement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.Hashtable;
import java.util.ArrayList;
public class OdbcConnection
{
	private Connection connection;
	private String connectionString;
	private String properConnectionString;
	private boolean connected;
	private Hashtable<String, String> properties;
	private DataTable[] tables;
	public OdbcConnection()
	{
		this.connectionString = null;
		this.properties = new Hashtable<String, String>();
		this.properties.put("driver", "");
		this.properties.put("database", "");
		this.properties.put("username", "");
		this.properties.put("password", "");
		try
		{
			Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
		}
		catch(ClassNotFoundException ex)
		{
			/* There is no need to catch this exception as the
			 * occurance of the sun.jdbc.odbc.JdbcOdbcDriver
			 * class is guaranteed with every JRE.
			 */
		}
	}
	private String getEquivalentPropertyName(String type, String name)
	{
		if(type.equalsIgnoreCase("connection"))
		{
			if(name.equalsIgnoreCase("jdbc:odbc:Driver") || 
			  name.equalsIgnoreCase("driver"))
			  name = "driver";
			else if(name.equalsIgnoreCase("dbq") || 
			  name.equalsIgnoreCase("database") || 
			  name.equalsIgnoreCase("datasource") || 
			  name.equalsIgnoreCase("data source") || 
			  name.equalsIgnoreCase("initial catalog"))
			  name = "database";
			else if(name.equalsIgnoreCase("uid") || 
			  name.equalsIgnoreCase("user id") || 
			  name.equalsIgnoreCase("username"))
			  name = "username";
			else if(name.equalsIgnoreCase("pwd") || 
			  name.equalsIgnoreCase("password"))
			  name = "password";
		}
		return name;
	}
	public void setConnectionString(String connectionString)
	{
		this.connectionString = connectionString;
		if(connectionString.indexOf(";")!=-1)
		{
			String[] tokens = connectionString.split(";");
			for(String token : tokens)
			{
				if(token.indexOf("=")!=-1)
				{
					String[] values = token.split("=");
					if(values.length == 2)
					{
						values[0] = getEquivalentPropertyName("connection", values[0]);
						properties.put(values[0], values[1]);
					}
					else if(values.length == 1)
					{
						values[0] = getEquivalentPropertyName("connection", values[0]);
						properties.put(values[0], "");
					}
					if(!properties.get("driver").equals("") && !properties.get("database").equals(""))
					  this.properConnectionString = "jdbc:odbc:Driver=" + properties.get("driver") +
					    ";DBQ=" + properties.get("database");
					else this.properConnectionString = null;
				}
				else
				{
					/* TODO: Have to pass in an exception or something like that if a
					 * connection string is discovered as being malformed.
					 */
					this.connectionString = null;
				}
			}
		}
	}
	public String getConnectionString()
	{
		return this.connectionString;
	}
	public boolean open()
	{
		this.connected = false;
		if(this.connectionString != null && this.properConnectionString != null)
		{
			connection = null;
			try
			{
				if(properties.get("username").equals(""))
				  this.connection = DriverManager.getConnection(this.properConnectionString);
				else this.connection = DriverManager.getConnection(this.properConnectionString, 
				  properties.get("username"), properties.get("password"));
				this.connected = (connection != null);
			}
			catch(SQLException ex)
			{
				this.connected = false;
			}
		}
		else
		{
			/* Returns a warning if no connection string is specified.
			 */
			this.connected = false;
		}
		if(this.connected)
		{
			try
			{
				DatabaseMetaData metaData = this.connection.getMetaData();
				ResultSet resultSet = metaData.getTables(
				  null, null, null, new String[]{"TABLE"});
				ArrayList<DataTable> dataTables = new ArrayList<DataTable>();
				while(resultSet.next())
				{
					DataTable table = new DataTable(resultSet.getString("TABLE_NAME"));
					dataTables.add(table);
				}
				this.tables = new DataTable[dataTables.size()];
				int count = 0;
				for(DataTable table : dataTables)
				{
					this.tables[count++] = table;
				}
				try
				{
					for(DataTable table : this.tables)
					{
						Statement statement = this.connection.createStatement();
						resultSet = statement.executeQuery("SELECT * FROM " + table.getName());
						ResultSetMetaData rsMetaData = resultSet.getMetaData();
						DataColumn[] columns = new DataColumn[rsMetaData.getColumnCount()];
						for(count=1; count<=columns.length; ++count)
						{
							DataColumn column = new DataColumn(
							  rsMetaData.getColumnName(count));
							column.setAutoIncrementing(rsMetaData.isAutoIncrement(count));
							switch(rsMetaData.isNullable(count))
							{
								case ResultSetMetaData.columnNoNulls: column.setNullable(false); break;
								case ResultSetMetaData.columnNullable: column.setNullable(true); break;
								default: column.setNullable(false);
							}
							column.setAutoIncrementing(rsMetaData.isAutoIncrement(count));
							/* 
							 * TODO: Have to add more column-specific tasks to extract data from
							 * the database into this column's meta-data.
							 */
							columns[count-1] = column;
						}
						table.setColumns(columns);
					}
				}
				/*catch(ClassNotFoundException ex)
				{
					 * Might not occur that often. But if a column's data-type is not
					 * known, this exception is thrown.
					 *
				}*/
				catch(SQLException ex)
				{
					/* Catches an exception if the meta-data for the table's individual
					 * can not be properly read.
					 */
					System.out.println("SCHMTNC:" + ex.getMessage());
				}
			}
			catch(SQLException ex)
			{
				/* Catches an exception if there is no meta-data associated with
				 * the database linked to the connection being used.
				 */
			}
		}
		return this.connected;
	}
	public boolean isOpen()
	{
		return this.connected;
	}
	public Object get(String identifier)
	{
		if(identifier.equalsIgnoreCase("TABLES"))
		{
			return this.getTables();
		}
		else return null;
	}
	public DataTable[] getTables()
	{
		return this.tables;
	}
}